import usePortal from "../use/usePortal";
import useAlignPostion from "../use/useAlignPostion";
import usezIndex from "../use/usezIndex";
import useTransition from "../use/useTransition";
import type { ButtonProps } from "../Button";
import { isColor } from "../utils/assist";
import { computed, CSSProperties, defineComponent, HTMLAttributes, onBeforeUnmount, onMounted, PropType, ref, Teleport, VNode, watchEffect } from "vue";
import { uuidv4 } from "../utils/assist";
import Space from "../Space";
import Button from "../Button";
import useClickOutside from "../use/useClickOutside";

export interface InnerPopupProps extends Omit<HTMLAttributes, 'title'> {
    align?: 'top'|'bottom'|'left'|'right'|'topLeft'|'topRight'|'bottomLeft'|'bottomRight'|'leftTop'|'leftBottom'|'rightTop'|'rightBottom'
    trigger?: 'hover'|'click'|'focus'
    disabled?: boolean
    arrow?: boolean
    hideDelay?: number
    onVisibleChange?: (open: boolean) => void
    content?: string | number | JSX.Element
    contentStyle?: CSSProperties | string
    modelValue?: boolean
    theme?: string |'light'|'primary'|'success'|'info'|'warning'|'error'|'blue'|'green'|'red'|'yellow'|'pink'|'magenta'|'volcano'|'orange'|'gold'|'lime'|'cyan'|'geekblue'|'purple',
    confirm?: boolean
    okText?: any
    okType?: ButtonProps['type']
    title?: string | number | JSX.Element
    cancelText?: any
    cancelType?: ButtonProps['type']
    offset?: number
    clsPrefix?: string
    varName?: string
    onOk?: () => void | Promise<boolean|void>
    onCancel?: () => void
    arrowPointAtCenter?: boolean
    showCancel?: boolean,
    icon?: VNode
}

export const innerPopupDefinedProps = {
    align: {type: String as PropType<InnerPopupProps['align']>},
    trigger: {type: String as PropType<InnerPopupProps['trigger']>},
    disabled: {type: Boolean as PropType<InnerPopupProps['disabled']>},
    arrow: {type: Boolean as PropType<InnerPopupProps['arrow']>, default: undefined},
    hideDelay: {type: Number as PropType<InnerPopupProps['hideDelay']>},
    content: {type: [String, Number, Object] as PropType<InnerPopupProps['content']>},
    contentStyle: {type: [Object, String] as PropType<InnerPopupProps['contentStyle']>},
    modelValue: {type: Boolean as PropType<InnerPopupProps['modelValue']>},
    theme: {type: [String, Object] as PropType<InnerPopupProps['theme']>},
    confirm: {type: Boolean as PropType<InnerPopupProps['confirm']>, default: undefined},
    okText: {type: [String, Object] as PropType<InnerPopupProps['okText']>},
    okType: {type: String as PropType<InnerPopupProps['okType']>},
    title: {type: [String, Number, Object] as PropType<InnerPopupProps['title']>},
    cancelText: {type: [String, Object] as PropType<InnerPopupProps['cancelText']>},
    cancelType: {type: String as PropType<InnerPopupProps['cancelType']>},
    offset: {type: Number as PropType<InnerPopupProps['offset']>},
    clsPrefix: {type: String as PropType<InnerPopupProps['clsPrefix']>},
    varName: {type: String as PropType<InnerPopupProps['varName']>},
    arrowPointAtCenter: {type: Boolean as PropType<InnerPopupProps['arrowPointAtCenter']>},
    showCancel: {type: Boolean as PropType<InnerPopupProps['showCancel']>},
    onOk: {type: Function as PropType<InnerPopupProps['onOk']>},
    onCancel: {type: Function as PropType<InnerPopupProps['onCancel']>},
    icon: {type: Object as PropType<InnerPopupProps['icon']>}
};

export const InnerPopup = defineComponent({
    name: 'InnerPopup',
    props: innerPopupDefinedProps,
    emits: ['update:modelValue', 'visibleChange'],
    setup (props: InnerPopupProps, {emit, slots, attrs, expose}) {
        const visible = ref(props.modelValue);
        const opened = ref(visible.value);
        const _ = ref(uuidv4());
        const buttonLoading = ref(false);
        const clsPrefix = props.clsPrefix ?? 'cm-popover';
        const varName = props.varName ?? 'popover';

        const inner = ref();
        let nextElementSibling: any;
        let removeClickOutside: () => void;
        const wrap = ref();

        const offset = () => props.offset || 0;
        const align = () => props.align || 'top';
        const trigger = () => props.confirm ? 'click' : props.trigger || 'hover';
        const zindex = usezIndex();

        let timer: any = null;
        const hideDelay = props.hideDelay || 200;
        const clearDelayTimer = () => {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
        };

        watchEffect(() => {
            visible.value = props.modelValue;
        });

        const setVisibleState = (vis: boolean) => {
            visible.value = vis;
            emit('update:modelValue', vis);
            emit('visibleChange', vis);
        };

        const onMouseEnter = () => {
            if (props.disabled) {
                return;
            }
            if (trigger() === 'hover') {
                clearDelayTimer();
                setVisibleState(true);
            }
        };

        const onPopMouseEnter = () => {
            if (trigger() === 'hover') {
                clearDelayTimer();
            }
        };

        const onMouseLeave = () => {
            if (props.disabled) {
                return;
            }
            if (trigger() === 'hover') {
                timer = setTimeout( () => {
                    setVisibleState(false);
                }, hideDelay);
            }
        };

        const onPopMouseLeave = () => {
            onMouseLeave();
        };

        const onFocus = () => {
            if (props.disabled) {
                return;
            }
            clearDelayTimer();
            if (trigger() === 'focus') {
                setVisibleState(true);
            }
        };

        const onBlur = () => {
            if (trigger() === 'focus') {
                timer = setTimeout( () => {
                    setVisibleState(false);
                }, hideDelay);
            }
        };

        const onClick = (e: any) => {
            if (props.disabled) {
                return;
            }
            e.preventDefault();
            e.stopPropagation();
            if (trigger() === 'click') {
                const show = visible.value;
                setVisibleState(!show);
            }
        };

        const color = isColor(props.theme) ? '' : props.theme;
        const classList = computed(() => ({
            [`${clsPrefix}-inner`]: true,
            [`${clsPrefix}-with-arrow`]: props.arrow,
            [`${clsPrefix}-with-arrow-center`]: props.arrowPointAtCenter,
            [`${clsPrefix}-confirm`]: props.confirm,
            [`${clsPrefix}-${color}`]: color,
        }));

        const transition = useTransition({
            el: ()=> wrap.value,
            startClass: `${clsPrefix}-inner-visible`,
            activeClass: `${clsPrefix}-inner-show`,
            onLeave: () => {
                opened.value = false;
            },
            onEnter: () => {
                opened.value = true;
            }
        });

        watchEffect(() => {
            const v = visible.value;
            if (v) {
                transition.enter();
            } else {
                transition.leave();
            }
        });

        const posStyle = () => {
            opened.value;
            _.value;
            if (nextElementSibling) {
                let placement = align();
                if (props.arrowPointAtCenter) {
                    if (['top', 'topLeft', 'topRight'].includes(placement)) {
                        placement = 'top';
                    }
                    if (['bottom', 'bottomLeft', 'bottomRight'].includes(placement)) {
                        placement = 'bottom';
                    }
                    if (['left', 'leftTop', 'leftBottom'].includes(placement)) {
                        placement = 'left';
                    }
                    if (['right', 'rightTop', 'rightBottom'].includes(placement)) {
                        placement = 'right';
                    }
                }
                const pos: any = useAlignPostion(placement, nextElementSibling);
                pos.top = pos.top + document.documentElement.scrollTop + 'px';
                pos.left = pos.left + document.documentElement.scrollLeft + 'px';
                pos['z-index'] = zindex;
                Object.assign(pos, {
                    [`--cui-${varName}-background-color`]: isColor(props.theme) ? props.theme : '',
                    [`--cui-${varName}-offset`]: `${offset()}px`,
                });
                return pos;
            }
        };

        const onOk = async () => {
            buttonLoading.value = true;
            const ret = await props.onOk?.();
            buttonLoading.value = false;
            if (ret === undefined || ret === true) {
                setVisibleState(false);
            }
        };

        const onCancel = () => {
            props.onCancel?.();
            setVisibleState(false);
        };

        onMounted(() => {
            nextElementSibling = inner.value.nextElementSibling;
            // inner.value.parentNode.removeChild(inner.value);
            if (nextElementSibling) {
                if (trigger() === 'hover') {
                    nextElementSibling.addEventListener('mouseenter', onMouseEnter, false);
                    nextElementSibling.addEventListener('mouseleave', onMouseLeave, false);
                }
                if (trigger() === 'click') {
                    nextElementSibling.addEventListener('click', onClick, false);
                    removeClickOutside = useClickOutside([wrap.value, nextElementSibling], () => {
                        if (buttonLoading.value) {
                            return;
                        }
                        setVisibleState(false);
                    });
                }
                if (trigger() === 'focus') {
                    nextElementSibling.addEventListener('focus', onFocus, false);
                    nextElementSibling.addEventListener('blur', onBlur, false);
                }
            }
        });

        onBeforeUnmount(() => {
            if (nextElementSibling) {
                if (trigger() === 'hover') {
                    nextElementSibling.removeEventListener('mouseenter', onMouseEnter);
                    nextElementSibling.removeEventListener('mouseleave', onMouseLeave);
                }
                if (trigger() === 'click') {
                    nextElementSibling.removeEventListener('click', onClick);
                }
                if (trigger() === 'focus') {
                    nextElementSibling.removeEventListener('focus', onFocus, false);
                    nextElementSibling.removeEventListener('blur', onBlur, false);
                }
            }
            removeClickOutside && removeClickOutside();
        });

        expose({
            updatePosition () {
                _.value = uuidv4();
            }
        });

        const id = `${clsPrefix}-portal`;
        const okText = props.okText ?? '确 定';
        const cancelText = props.cancelText ?? '取 消';
        return () => <>
            <span style={{display: 'none'}} ref={inner} />
            {slots.default?.()}
            <Teleport to={usePortal(id, id)}>
                <div ref={wrap} style={posStyle()} x-placement={align()} class={classList.value}
                    onMouseenter={onPopMouseEnter} onMouseleave={onPopMouseLeave} {...attrs}>
                    {
                        props.title ? <div class={`${clsPrefix}-title`}>{props.title}</div> : null
                    }
                    <div class={`${clsPrefix}-body`} style={props.contentStyle}>
                        {props.content}
                    </div>
                    {
                        props.confirm
                            ? <Space class={`${clsPrefix}-tools`} justify="end">
                                {
                                    props.showCancel
                                        ? <Button type={props.cancelType || 'default'} size="small" onClick={onCancel}>{cancelText}</Button>
                                        : null
                                }
                                <Button type={props.okType || 'primary'} size="small" onClick={onOk} loading={buttonLoading.value}>{okText}</Button>
                            </Space>
                            : null
                    }
                    {
                        props.arrow
                            ? <svg width="24" height="8" xmlns="http://www.w3.org/2000/svg" class={`${clsPrefix}-icon-arrow`}>
                                <path d="M0.5 0L1.5 0C1.5 4, 3 5.5, 5 7.5S8,10 8,12S7 14.5, 5 16.5S1.5,20 1.5,24L0.5 24L0.5 0z" opacity="1" />
                                <path d="M0 0L1 0C1 4, 2 5.5, 4 7.5S7,10 7,12S6 14.5, 4 16.5S1,20 1,24L0 24L0 0z" />
                            </svg>
                            : null
                    }
                </div>
            </Teleport>
        </>;
    }
});
